﻿using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Renci.SshNet.Common;
using Volo.Abp.DependencyInjection;
using Z.EntityFramework.Plus;

namespace OpenPasteSpider
{
    /// <summary>
    /// 管理所有的SSH链接和执行命令
    /// </summary>
    public class LinkHelper : ISingletonDependency, IDisposable
    {

        /// <summary>
        /// 
        /// </summary>
        private System.Collections.Generic.Dictionary<int, LinkItem> _hashtable;

        /// <summary>
        /// 
        /// </summary>
        private IServiceProvider _serviceProvider;

        /// <summary>
        /// 
        /// </summary>
        private ILogger<LinkHelper> _logger;

        /// <summary>
        /// 
        /// </summary>
        private readonly SpiderConfig _config;

        /// <summary>
        /// 并发资源锁
        /// </summary>
        private SemaphoreSlim slimelock;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="logger"></param>
        /// <param name="config"></param>
        public LinkHelper(IServiceProvider serviceProvider, ILogger<LinkHelper> logger, IOptions<SpiderConfig> config)
        {
            _hashtable = new System.Collections.Generic.Dictionary<int, LinkItem>();
            _serviceProvider = serviceProvider;
            _logger = logger;
            _config = config.Value;
            slimelock = new SemaphoreSlim(1);
        }


        /// <summary>
        /// 获取对象
        /// </summary>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        private LinkItem TakeLink(int linuxid)
        {

            try
            {
                slimelock.Wait();
                if (_hashtable.ContainsKey(linuxid))
                {
                    var info = _hashtable[linuxid];
                    return info;
                }
                else
                {
                    using var _scope = _serviceProvider.CreateScope();
                    using var _dbContext = _scope.ServiceProvider.GetRequiredService<IOpenPasteSpiderDbContext>();
                    var one = _dbContext.LinuxInfo.Where(x => x.Id == linuxid).AsNoTracking().FirstOrDefault();

                    if (one != null && one != default)
                    {
                        var item = new LinkItem(_logger, one, _config.UserToken);
                        item.OnEvented += Item_OnEvented;
                        _hashtable.Add(linuxid, item);
                        return item;
                    }
                    else
                    {
                        throw new PasteCodeException($"linux的配置有误,无法找到这个服务器的信息:{linuxid}，无法执行链接的创建动作！");
                    }
                }

            }
            finally
            {
                slimelock.Release();
            }
        }

        /// <summary>
        /// 内部返回的信息
        /// </summary>
        /// <param name="linuxid"></param>
        /// <param name="code"></param>
        /// <param name="msg"></param>
        private void Item_OnEvented(int linuxid, string code, string msg)
        {
            if (code == "400")
            {
                //写入到数据库中
                using var _scope = _serviceProvider.CreateScope();
                using var _dbContext = _scope.ServiceProvider.GetRequiredService<IOpenPasteSpiderDbContext>();
                _dbContext.LinuxInfo.Where(x => x.Id == linuxid).Update(x => new projectmodel.LinuxInfo { LinkCode = msg });
            }
            else
            {
                //记录到日志中
                _logger.LogError(msg);
            }
        }

        /// <summary>
        /// 移除Linux的信息，一般是有信息变更了
        /// </summary>
        /// <param name="linuxid"></param>
        public void SetLinuxUpdate(int linuxid)
        {
            if (_hashtable != null)
            {
                if (_hashtable.ContainsKey(linuxid))
                {
                    //停止所有的链接
                    var item = _hashtable[linuxid];
                    if (item != null)
                    {
                        item.Dispose();
                    }
                    //重新读取信息
                    _hashtable.Remove(linuxid);
                }
            }
        }


        public delegate void ReadStream(string guid, string msg);
        public event ReadStream OnStreamReviced;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="linuxid"></param>
        /// <param name="commandstr"></param>
        /// <returns></returns>
        public async Task<(string msg, bool ok, int code)> RunSingleCommand(int linuxid, string commandstr)
        {
            var stop = new System.Diagnostics.Stopwatch();
            stop.Start();
            var link = TakeLink(linuxid);
            if (link != null)
            {
                stop.Stop();
                //Console.WriteLine($"执行LinkHelper.RunSingleCommand.TakeLink花费{stop.ElapsedMilliseconds}ms");
                return await link.RunSingleCommand(commandstr);
            }
            else
            {
                return ("链接异常，请确认", false, 500);
            }

        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="linuxid"></param>
        /// <param name="commandstr"></param>
        /// <returns></returns>
        public async Task<(string msg, bool ok, int code)> RunSingleCommandAsync(int linuxid, string commandstr)
        {
            try
            {
                var link = TakeLink(linuxid);
                if (link != null)
                {
                    return await link.RunSingleCommand(commandstr);
                }
                else
                {
                    return ("链接异常，请确认", false, 500);
                }
            }
            catch (SshException exl)
            {
                RemoveLinux(linuxid);
                _logger.LogException(exl);
                return (exl.Message, false, 500);
            }
        }

        /// <summary>
        /// 移除某一个服务器
        /// </summary>
        /// <param name="linuxid"></param>
        public void RemoveLinux(int linuxid)
        {
            if (_hashtable != null)
            {
                if (_hashtable.ContainsKey(linuxid))
                {
                    var item = _hashtable[linuxid];
                    _hashtable.Remove(linuxid);
                    if (item != null)
                    {
                        item.Dispose();
                    }
                }
            }
        }

        /// <summary>
        /// 判定文件是否存在 返回文件的基本信息
        /// </summary>
        /// <param name="linuxid"></param>
        /// <param name="file"></param>
        /// <returns></returns>
        public async Task<(bool ok, string response)> FileExits(int linuxid, string file)
        {
            var link = TakeLink(linuxid);
            if (link != null)
            {
                var back = await link.RunSingleCommand($"stat -f {file}");
                return (back.ok, back.msg);
            }
            return (false, "未能获取到链接信息");
        }

        /// <summary>
        /// 读取远端的一个文件的内容
        /// </summary>
        /// <param name="linuxid"></param>
        /// <param name="file"></param>
        /// <returns></returns>
        public async Task<(bool, string)> ReadFile(int linuxid, string file)
        {
            var link = TakeLink(linuxid);
            if (link != null)
            {
                var back = await link.ReadFile(file);
                return (back.Item1, back.Item2);
            }
            return (false, "未能获取到链接信息");
        }




        /// <summary>
        /// 判断是否能链接
        /// </summary>
        /// <param name="linuxid"></param>
        /// <returns></returns>
        public async Task<(bool ok, string response)> DockerCheck(int linuxid)
        {
            var link = TakeLink(linuxid);
            if (link != null)
            {
                var back = await link.RunSingleCommand($"docker --version");
                return (back.ok, back.msg);
            }
            return (false, "未能获取到链接信息");
        }


        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (_hashtable != null)
            {
                foreach (var item in _hashtable.Values)
                {
                    ((LinkItem)item).Dispose();
                }
                _hashtable.Clear();
            }
        }
    }

}
